Librerias

Primero incorporamos las librerias necesarias para poder ejecutar el presente notebook.

# Librerias
#install.packages('gganimate')
#install.packages("heatmaply")
#install.packages('gifski')
#install.packages('png')
#install.packages('units')
#install.packages('sf')
#install.packages('transformr')
require(tidyverse)
Loading required package: tidyverse
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.4     ✓ dplyr   1.0.7
✓ tidyr   1.1.4     ✓ stringr 1.4.0
✓ readr   2.0.1     ✓ forcats 0.5.1
── Conflicts ────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
require(geosphere)
Loading required package: geosphere
#<<<<<<< Updated upstream
require(rvest)
Loading required package: rvest

Attaching package: ‘rvest’

The following object is masked from ‘package:readr’:

    guess_encoding
require(gganimate)
Loading required package: gganimate
require(lubridate)
Loading required package: lubridate

Attaching package: ‘lubridate’

The following objects are masked from ‘package:base’:

    date, intersect, setdiff, union
require(heatmaply)
Loading required package: heatmaply
Loading required package: plotly

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout

Loading required package: viridis
Loading required package: viridisLite

======================
Welcome to heatmaply version 1.3.0

Type citation('heatmaply') for how to cite the package.
Type ?heatmaply for the main documentation.

The github page is: https://github.com/talgalili/heatmaply/
Please submit your suggestions and bug-reports at: https://github.com/talgalili/heatmaply/issues
You may ask questions at stackoverflow, use the r and heatmaply tags: 
     https://stackoverflow.com/questions/tagged/heatmaply
======================
require(transformr)
Loading required package: transformr
Registered S3 methods overwritten by 'proxy':
  method               from    
  print.registry_field registry
  print.registry_entry registry
require(gifski)
Loading required package: gifski
require(png)
Loading required package: png
require(ggplot2)
#=======
require(rvest) # Cargamos el paquete
require(lubridate)
#>>>>>>> Stashed changes

Importar datos

Posteriormente importamos los datos que queremos analizar y los formateamos.

# Llamamos a los DataSets

datos_2021 = read.csv('../TP_LaboDeDatos/Data/sna_abril_2021_fixed_encoding.csv', encoding = 'UTF-8', sep = ',')

# Concateno todos los datos
datos2= read_csv2('../TP_LaboDeDatos/Data/202109-informe-ministerio.csv')
ℹ Using "','" as decimal and "'.'" as grouping mark. Use `read_delim()` for more control.
Rows: 219803 Columns: 11
── Column specification ────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ";"
chr  (9): Fecha, Clase de Vuelo (todos los vuelos), Clasificación Vuelo, Tipo de Movimiento, Aeropuerto, Ori...
dbl  (1): Pasajeros
time (1): Hora UTC

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
datos2$Fecha_completa = strptime(paste(datos2$Fecha, datos2$`Hora UTC`), format = "%d/%m/%Y %H:%M:%S")

En esa celda importamos los datos de los aeropuertos.

# Utilizamos la tabla que se encuentra en 'https://en.wikipedia.org/wiki/List_of_airports_in_Argentina'
# para acceder a las variables de ciudad, provincia y coordenadas de cada aeropuerto 

aeropuertos_wiki = read_html('https://en.wikipedia.org/wiki/List_of_airports_in_Argentina')
elemento_tabla   = html_element(aeropuertos_wiki,'.wikitable')
aeropuertos      = html_table(elemento_tabla)

Particularmente, necesitamos un procesamiento especial para obtener las coordenadas de cada aeropuerto en un formato que nos sea util.

# Corrijo la columna de coordenadas 

separar     = strsplit((aeropuertos$Coordinates), '/') # Divide a los strings en los lugares donde haya '/' 
coordenadas = sapply(separar, function(x) x[3])        # Me quedo solo con el 3er tipo de coordenada
coordenadas = gsub('[^0-9,.,-]','', coordenadas)           # Elimino los caracteres que no quiero utilizar

aeropuertos = aeropuertos %>% 
  mutate(lat = as.numeric(substr(coordenadas, 1, 9)), long = as.numeric(substr(coordenadas, 10, 18))) # Separo a mano latitud y longitud (revizar si esta todo en orden)

aeropuertos = filter(aeropuertos, nchar(ICAO)>1)
aeropuertos = aeropuertos[order(aeropuertos$ICAO),]

Procesar datos

En la siguiente celda lo que hacemos es a cada vuelo le agregamos los datos relativos a sus aeropuertos de origen y destino (si los tenemos). Además, calculamos con ellos la distancia recorrida por el vuelo.


for(i in 1:length(aeropuertos$ICAO)){
  inds = datos2$Aeropuerto==aeropuertos$IATA[i]
  datos2[inds,c('ciudad_origen','provincia_origen','lat_origen','long_origen')] = aeropuertos[i,c("City served","Province","lat","long")]
  
  inds = datos2$`Origen / Destino`==aeropuertos$IATA[i]
  datos2[inds,c('ciudad_destino','provincia_destino','lat_destino','long_destino')] = aeropuertos[i,c("City served","Province","lat","long")]
}
  for(i in 1:length(datos_2021$ana)){
  inds = datos2$Aeropuerto==datos_2021$ana[i]
  datos2[inds,c('ciudad_origen','provincia_origen','lat_origen','long_origen')] = datos_2021[i,c("nam","cpr","x","y")]
  
  
  inds = datos2$`Origen / Destino`==datos_2021$ana[i]
  datos2[inds,c('ciudad_destino','provincia_destino','lat_destino','long_destino')] = datos_2021[i,c("nam","cpr","x","y")]
  
  inds = datos2$Aeropuerto==datos_2021$iko[i]
  datos2[inds,c('ciudad_destino','provincia_destino','lat_destino','long_destino')] = datos_2021[i,c("nam","cpr","x","y")]
  
  inds = datos2$`Origen / Destino`==datos_2021$iko[i]
  datos2[inds,c('ciudad_destino','provincia_destino','lat_destino','long_destino')] = datos_2021[i,c("nam","cpr","x","y")]
}

datos2 = drop_na(datos2)
datos2$distancia = distHaversine(datos2[,c("long_origen","lat_origen")],datos2[,c("long_destino","lat_destino")])/1000

datos2 = datos2 %>% 
  filter(`Aerolinea Nombre` != 0)

Dividimos los eventos del dataset de vuelos en aterrizajes y despegues.


datos2$fecha_hora = strptime(paste(datos2$Fecha, datos2$`Hora UTC`), format = "%d/%m/%Y %H:%M:%S")
despegues = datos2[datos2$`Tipo de Movimiento` == 'Despegue',]
aterrizajes = datos2[datos2$`Tipo de Movimiento` == 'Aterrizaje',]

Ahora tratamos de unir los aterrizajes y despegues con el objetivo de poder identificar todo el recorrido de un vuelo. (particularmente la hora de salida y de llegada, que son los datos que no nos dan directamente en el dataset)

# dividimos en despegues y aterrizajes


matched = left_join(despegues, aterrizajes, by= c("Aeropuerto" = "Origen / Destino", "Origen / Destino" = "Aeropuerto", "Aerolinea Nombre" = "Aerolinea Nombre", "Aeronave" = "Aeronave")) %>% 
  mutate(tdif = as.numeric(fecha_hora.y - fecha_hora.x, units='hours')) %>%
  group_by(Aeropuerto, fecha_hora.x, Aeronave, `Aerolinea Nombre`) %>%
  filter(tdif > 0) %>% 
  filter(tdif < 5) %>% 
  filter(tdif == min(tdif))

matched = matched %>%
  mutate(vel_media = distancia.y/tdif)

Preguntas

Concentración del mercado

Las primeras preguntas que nos hemos planteado son relativas al grado de concentración que tienen los vuelos.

Por esto, la primera pregunta que nos interesa responder es ver cuales aeropuertos se conectan más y cuales menos. Además, notando que los casos donde el vuelo despega y aterriza en el mismo lugar son muy posiblemente vuelos de entrenamiento.

aers = unique(datos2$Aeropuerto)
mat = matrix(nrow = length(aers), ncol = length(aers))
colnames(mat) = aers
row.names(mat) = aers
for(i in 1:length(aers)){
  for(j in 1:length(aers)){
    mat[i,j]  =  sum((datos2$Aeropuerto==aers[i]) & (datos2$`Origen / Destino`==aers[j]))
  }
}
heatmaply(mat, Rowv = NA, Colv = NA,  col_dend_up=FALSE)%>% 
  layout(xaxis = list(side = "top"))

Ahora vamos a ver qué ocurre con el grafico anterior si lo pasamos a escala logaritmica (agregando 1 para evitar los -inf)

Como se puede ver, hay mayor cantidad de relaciones que se vuelven visibles y diferenciables gracias al cambio de escala.

Resalta el aeropuerto FDO como un importante centro de entrenamiento. Por otro lado, se observa como la conexión más fuerte es claramente entre Aeroparque y Bariloche.

La siguiente pregunta será relativa a la concentración entre distintas aerolineas.

Y ahora probamos de repetir el ejercicio de expresar las distintas cantidades en escala logaritmica para ver el efecto.

En este nuevo grafico se confirma la idea de que hay una enorme concentración de los vuelos en pocas empresas.

En este tercer grafico solo mostramos las más importantes para que sea entendible la información, además de que tiene una función más sintetica que los anteriores.

x = 8
apps2 = apps[1:x]
apps2[x+1] = sum(apps[x:length(apps)])
names(apps2)[x+1] = "OTRAS"
names(apps2) = paste(names(apps2),apps2)

fig <- plot_ly(data = as.data.frame(apps2), labels = names(apps2), values = apps2, type = 'pie', sort=FALSE)
fig <- fig %>% layout(title = 'Cantidad de vuelos por aerolinea',
         xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
         yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE)) 
fig

Tiempo en el aire

Las siguientes preguntas que nos hemos planteado feron las relativas a cuando los aviones estaban volando y cuando no.

La primera fue decir exactamente en qué horas del año estuvieron más y menos aviones en el aire. Para eso primero hay que agregar una hora universal a los vuelos

Para evitar el ruido y las pequeñas variaciones diarias y semanales, vamos a repetir el grafico suavizado por día y por semana.

En el grafico con el promedio semanal movil se puede apreciar especialmente la caida en la cantidad de aviones en el aire durante la mitad del año

Ahora cual es el horario en el que los pasajeros viajan mas

require(gganimate)
horario_vuelo = datos2 %>%
  filter(datos2$Pasajeros > 0) %>% 
  group_by(horario = hour(`Hora UTC`)) %>% 
  summarise(pasajeros = mean(Pasajeros))

table(horario_vuelo$horario)

 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 
 1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1 
plot = horario_vuelo %>% 
  ggplot(aes(x = horario, y = pasajeros, color = pasajeros)) + geom_line() +
    geom_point() +
  transition_reveal(horario)+labs(title = 'Cantidad promedio de pasajeros a lo largo del dia', x = 'Hora', y = 'Cantidad promedio de pasajeros')+ shadow_mark()+ scale_x_continuous(breaks = round(seq(min(horario_vuelo$horario), max(horario_vuelo$horario), by = 1),1))
animate(plot, renderer = gifski_renderer(loop = T))

Rendering [=>--------------------------------------------------------------------------] at 5.8 fps ~ eta: 17s
Rendering [=>--------------------------------------------------------------------------] at 6.4 fps ~ eta: 15s
Rendering [==>-------------------------------------------------------------------------] at 6.8 fps ~ eta: 14s
Rendering [===>--------------------------------------------------------------------------] at 7 fps ~ eta: 14s
Rendering [====>-----------------------------------------------------------------------] at 7.2 fps ~ eta: 13s
Rendering [====>-----------------------------------------------------------------------] at 7.3 fps ~ eta: 13s
Rendering [=====>----------------------------------------------------------------------] at 7.3 fps ~ eta: 13s
Rendering [======>---------------------------------------------------------------------] at 7.4 fps ~ eta: 12s
Rendering [=======>--------------------------------------------------------------------] at 7.5 fps ~ eta: 12s
Rendering [========>-------------------------------------------------------------------] at 7.6 fps ~ eta: 12s
Rendering [=========>------------------------------------------------------------------] at 7.5 fps ~ eta: 12s
Rendering [==========>-----------------------------------------------------------------] at 7.5 fps ~ eta: 11s
Rendering [==========>-----------------------------------------------------------------] at 7.4 fps ~ eta: 11s
Rendering [===========>----------------------------------------------------------------] at 7.4 fps ~ eta: 11s
Rendering [============>---------------------------------------------------------------] at 7.5 fps ~ eta: 11s
Rendering [=============>--------------------------------------------------------------] at 7.5 fps ~ eta: 11s
Rendering [==============>-------------------------------------------------------------] at 7.5 fps ~ eta: 11s
Rendering [===============>------------------------------------------------------------] at 7.5 fps ~ eta: 11s
Rendering [================>-----------------------------------------------------------] at 7.5 fps ~ eta: 10s
Rendering [================>-----------------------------------------------------------] at 7.6 fps ~ eta: 10s
Rendering [=================>----------------------------------------------------------] at 7.5 fps ~ eta: 10s
Rendering [==================>---------------------------------------------------------] at 7.5 fps ~ eta: 10s
Rendering [===================>--------------------------------------------------------] at 7.5 fps ~ eta: 10s
Rendering [====================>-------------------------------------------------------] at 7.5 fps ~ eta: 10s
Rendering [=====================>------------------------------------------------------] at 7.4 fps ~ eta: 10s
Rendering [======================>-----------------------------------------------------] at 7.4 fps ~ eta:  9s
Rendering [=======================>----------------------------------------------------] at 7.4 fps ~ eta:  9s
Rendering [========================>---------------------------------------------------] at 7.4 fps ~ eta:  9s
Rendering [=========================>--------------------------------------------------] at 7.4 fps ~ eta:  9s
Rendering [==========================>-------------------------------------------------] at 7.4 fps ~ eta:  9s
Rendering [===========================>------------------------------------------------] at 7.4 fps ~ eta:  9s
Rendering [============================>-----------------------------------------------] at 7.3 fps ~ eta:  8s
Rendering [=============================>----------------------------------------------] at 7.3 fps ~ eta:  8s
Rendering [==============================>---------------------------------------------] at 7.3 fps ~ eta:  8s
Rendering [===============================>--------------------------------------------] at 7.3 fps ~ eta:  8s
Rendering [================================>-------------------------------------------] at 7.3 fps ~ eta:  8s
Rendering [=================================>------------------------------------------] at 7.2 fps ~ eta:  8s
Rendering [==================================>-----------------------------------------] at 7.2 fps ~ eta:  7s
Rendering [===================================>----------------------------------------] at 7.2 fps ~ eta:  7s
Rendering [====================================>---------------------------------------] at 7.1 fps ~ eta:  7s
Rendering [=====================================>--------------------------------------] at 7.1 fps ~ eta:  7s
Rendering [======================================>-------------------------------------] at 7.1 fps ~ eta:  7s
Rendering [=======================================>------------------------------------] at 7.1 fps ~ eta:  7s
Rendering [========================================>-------------------------------------] at 7 fps ~ eta:  7s
Rendering [=========================================>------------------------------------] at 7 fps ~ eta:  7s
Rendering [==========================================>-----------------------------------] at 7 fps ~ eta:  6s
Rendering [===========================================>----------------------------------] at 7 fps ~ eta:  6s
Rendering [============================================>---------------------------------] at 7 fps ~ eta:  6s
Rendering [============================================>-------------------------------] at 6.9 fps ~ eta:  6s
Rendering [=============================================>------------------------------] at 6.9 fps ~ eta:  6s
Rendering [==============================================>-----------------------------] at 6.9 fps ~ eta:  6s
Rendering [===============================================>----------------------------] at 6.8 fps ~ eta:  5s
Rendering [================================================>---------------------------] at 6.8 fps ~ eta:  5s
Rendering [=================================================>--------------------------] at 6.8 fps ~ eta:  5s
Rendering [==================================================>-------------------------] at 6.8 fps ~ eta:  5s
Rendering [===================================================>------------------------] at 6.8 fps ~ eta:  5s
Rendering [===================================================>------------------------] at 6.7 fps ~ eta:  5s
Rendering [====================================================>-----------------------] at 6.7 fps ~ eta:  4s
Rendering [=====================================================>----------------------] at 6.7 fps ~ eta:  4s
Rendering [======================================================>---------------------] at 6.7 fps ~ eta:  4s
Rendering [=======================================================>--------------------] at 6.6 fps ~ eta:  4s
Rendering [========================================================>-------------------] at 6.6 fps ~ eta:  4s
Rendering [=========================================================>------------------] at 6.6 fps ~ eta:  4s
Rendering [==========================================================>-----------------] at 6.6 fps ~ eta:  3s
Rendering [===========================================================>----------------] at 6.6 fps ~ eta:  3s
Rendering [============================================================>---------------] at 6.5 fps ~ eta:  3s
Rendering [=============================================================>--------------] at 6.5 fps ~ eta:  3s
Rendering [==============================================================>-------------] at 6.5 fps ~ eta:  3s
Rendering [===============================================================>------------] at 6.5 fps ~ eta:  2s
Rendering [================================================================>-----------] at 6.5 fps ~ eta:  2s
Rendering [================================================================>-----------] at 6.4 fps ~ eta:  2s
Rendering [=================================================================>----------] at 6.4 fps ~ eta:  2s
Rendering [==================================================================>---------] at 6.4 fps ~ eta:  2s
Rendering [===================================================================>--------] at 6.4 fps ~ eta:  2s
Rendering [====================================================================>-------] at 6.4 fps ~ eta:  1s
Rendering [=====================================================================>------] at 6.4 fps ~ eta:  1s
Rendering [======================================================================>-----] at 6.3 fps ~ eta:  1s
Rendering [=======================================================================>----] at 6.3 fps ~ eta:  1s
Rendering [========================================================================>---] at 6.3 fps ~ eta:  1s
Rendering [=========================================================================>--] at 6.3 fps ~ eta:  0s
Rendering [==========================================================================>-] at 6.2 fps ~ eta:  0s
Rendering [============================================================================] at 6.2 fps ~ eta:  0s
                                                                                                              


Inserting image 1 at 0.00s (1%)...
Inserting image 2 at 0.10s (2%)...
Inserting image 3 at 0.20s (3%)...
Inserting image 4 at 0.30s (4%)...
Inserting image 5 at 0.40s (5%)...
Inserting image 6 at 0.50s (6%)...
Inserting image 7 at 0.60s (7%)...
Inserting image 8 at 0.70s (8%)...
Inserting image 9 at 0.80s (9%)...
Inserting image 10 at 0.90s (10%)...
Inserting image 11 at 1.00s (11%)...
Inserting image 12 at 1.10s (12%)...
Inserting image 13 at 1.20s (13%)...
Inserting image 14 at 1.30s (14%)...
Inserting image 15 at 1.40s (15%)...
Inserting image 16 at 1.50s (16%)...
Inserting image 17 at 1.60s (17%)...
Inserting image 18 at 1.70s (18%)...
Inserting image 19 at 1.80s (19%)...
Inserting image 20 at 1.90s (20%)...
Inserting image 21 at 2.00s (21%)...
Inserting image 22 at 2.10s (22%)...
Inserting image 23 at 2.20s (23%)...
Inserting image 24 at 2.30s (24%)...
Inserting image 25 at 2.40s (25%)...
Inserting image 26 at 2.50s (26%)...
Inserting image 27 at 2.60s (27%)...
Inserting image 28 at 2.70s (28%)...
Inserting image 29 at 2.80s (29%)...
Inserting image 30 at 2.90s (30%)...
Inserting image 31 at 3.00s (31%)...
Inserting image 32 at 3.10s (32%)...
Inserting image 33 at 3.20s (33%)...
Inserting image 34 at 3.30s (34%)...
Inserting image 35 at 3.40s (35%)...
Inserting image 36 at 3.50s (36%)...
Inserting image 37 at 3.60s (37%)...
Inserting image 38 at 3.70s (38%)...
Inserting image 39 at 3.80s (39%)...
Inserting image 40 at 3.90s (40%)...
Inserting image 41 at 4.00s (41%)...
Inserting image 42 at 4.10s (42%)...
Inserting image 43 at 4.20s (43%)...
Inserting image 44 at 4.30s (44%)...
Inserting image 45 at 4.40s (45%)...
Inserting image 46 at 4.50s (46%)...
Inserting image 47 at 4.60s (47%)...
Inserting image 48 at 4.70s (48%)...
Inserting image 49 at 4.80s (49%)...
Inserting image 50 at 4.90s (50%)...
Inserting image 51 at 5.00s (51%)...
Inserting image 52 at 5.10s (52%)...
Inserting image 53 at 5.20s (53%)...
Inserting image 54 at 5.30s (54%)...
Inserting image 55 at 5.40s (55%)...
Inserting image 56 at 5.50s (56%)...
Inserting image 57 at 5.60s (57%)...
Inserting image 58 at 5.70s (58%)...
Inserting image 59 at 5.80s (59%)...
Inserting image 60 at 5.90s (60%)...
Inserting image 61 at 6.00s (61%)...
Inserting image 62 at 6.10s (62%)...
Inserting image 63 at 6.20s (63%)...
Inserting image 64 at 6.30s (64%)...
Inserting image 65 at 6.40s (65%)...
Inserting image 66 at 6.50s (66%)...
Inserting image 67 at 6.60s (67%)...
Inserting image 68 at 6.70s (68%)...
Inserting image 69 at 6.80s (69%)...
Inserting image 70 at 6.90s (70%)...
Inserting image 71 at 7.00s (71%)...
Inserting image 72 at 7.10s (72%)...
Inserting image 73 at 7.20s (73%)...
Inserting image 74 at 7.30s (74%)...
Inserting image 75 at 7.40s (75%)...
Inserting image 76 at 7.50s (76%)...
Inserting image 77 at 7.60s (77%)...
Inserting image 78 at 7.70s (78%)...
Inserting image 79 at 7.80s (79%)...
Inserting image 80 at 7.90s (80%)...
Inserting image 81 at 8.00s (81%)...
Inserting image 82 at 8.10s (82%)...
Inserting image 83 at 8.20s (83%)...
Inserting image 84 at 8.30s (84%)...
Inserting image 85 at 8.40s (85%)...
Inserting image 86 at 8.50s (86%)...
Inserting image 87 at 8.60s (87%)...
Inserting image 88 at 8.70s (88%)...
Inserting image 89 at 8.80s (89%)...
Inserting image 90 at 8.90s (90%)...
Inserting image 91 at 9.00s (91%)...
Inserting image 92 at 9.10s (92%)...
Inserting image 93 at 9.20s (93%)...
Inserting image 94 at 9.30s (94%)...
Inserting image 95 at 9.40s (95%)...
Inserting image 96 at 9.50s (96%)...
Inserting image 97 at 9.60s (97%)...
Inserting image 98 at 9.70s (98%)...
Inserting image 99 at 9.80s (99%)...
Inserting image 100 at 9.90s (100%)...
Encoding to gif... done!
plot <- horario_vuelo %>% 
  ggplot(aes(x = horario, y = pasajeros, fill = pasajeros)) + geom_bar(stat='identity')+transition_states(horario,100,5)+shadow_mark()+labs(title = 'Cantidad promedio de pasajeros a lo largo del dia', x = 'Hora', y = 'Cantidad promedio de pasajeros')+ scale_x_continuous(breaks = round(seq(min(horario_vuelo$horario), max(horario_vuelo$horario), by = 1),1))
animate(plot, renderer = gifski_renderer(loop = T))

Rendering [=>----------------------------------------------------------------------------] at 7 fps ~ eta: 14s
Rendering [=>--------------------------------------------------------------------------] at 7.4 fps ~ eta: 13s
Rendering [==>-------------------------------------------------------------------------] at 7.7 fps ~ eta: 13s
Rendering [===>------------------------------------------------------------------------] at 7.8 fps ~ eta: 12s
Rendering [====>-----------------------------------------------------------------------] at 7.9 fps ~ eta: 12s
Rendering [====>-----------------------------------------------------------------------] at 7.7 fps ~ eta: 12s
Rendering [=====>----------------------------------------------------------------------] at 7.8 fps ~ eta: 12s
Rendering [======>---------------------------------------------------------------------] at 7.9 fps ~ eta: 12s
Rendering [=======>--------------------------------------------------------------------] at 7.9 fps ~ eta: 11s
Rendering [========>---------------------------------------------------------------------] at 8 fps ~ eta: 11s
Rendering [=========>--------------------------------------------------------------------] at 8 fps ~ eta: 11s
Rendering [==========>-------------------------------------------------------------------] at 8 fps ~ eta: 11s
Rendering [===========>------------------------------------------------------------------] at 8 fps ~ eta: 11s
Rendering [===========>----------------------------------------------------------------] at 8.1 fps ~ eta: 10s
Rendering [============>---------------------------------------------------------------] at 8.1 fps ~ eta: 10s
Rendering [=============>--------------------------------------------------------------] at 8.1 fps ~ eta: 10s
Rendering [==============>---------------------------------------------------------------] at 8 fps ~ eta: 10s
Rendering [===============>--------------------------------------------------------------] at 8 fps ~ eta: 10s
Rendering [================>-----------------------------------------------------------] at 8.1 fps ~ eta: 10s
Rendering [=================>----------------------------------------------------------] at 8.1 fps ~ eta:  9s
Rendering [==================>---------------------------------------------------------] at 8.1 fps ~ eta:  9s
Rendering [===================>--------------------------------------------------------] at 8.1 fps ~ eta:  9s
Rendering [====================>-------------------------------------------------------] at 8.1 fps ~ eta:  9s
Rendering [=====================>------------------------------------------------------] at 8.1 fps ~ eta:  9s
Rendering [======================>-----------------------------------------------------] at 8.1 fps ~ eta:  9s
Rendering [=======================>----------------------------------------------------] at 8.1 fps ~ eta:  9s
Rendering [=======================>----------------------------------------------------] at 8.1 fps ~ eta:  8s
Rendering [========================>---------------------------------------------------] at 8.1 fps ~ eta:  8s
Rendering [=========================>--------------------------------------------------] at 8.1 fps ~ eta:  8s
Rendering [==========================>-------------------------------------------------] at 8.1 fps ~ eta:  8s
Rendering [===========================>------------------------------------------------] at 8.1 fps ~ eta:  8s
Rendering [============================>-----------------------------------------------] at 8.1 fps ~ eta:  8s
Rendering [=============================>----------------------------------------------] at 8.1 fps ~ eta:  8s
Rendering [=============================>----------------------------------------------] at 8.1 fps ~ eta:  7s
Rendering [==============================>---------------------------------------------] at 8.1 fps ~ eta:  7s
Rendering [===============================>--------------------------------------------] at 8.1 fps ~ eta:  7s
Rendering [================================>-------------------------------------------] at 8.1 fps ~ eta:  7s
Rendering [=================================>------------------------------------------] at 8.1 fps ~ eta:  7s
Rendering [==================================>-----------------------------------------] at 8.2 fps ~ eta:  7s
Rendering [===================================>----------------------------------------] at 8.1 fps ~ eta:  7s
Rendering [===================================>----------------------------------------] at 8.1 fps ~ eta:  6s
Rendering [====================================>---------------------------------------] at 8.1 fps ~ eta:  6s
Rendering [=====================================>--------------------------------------] at 8.2 fps ~ eta:  6s
Rendering [======================================>-------------------------------------] at 8.2 fps ~ eta:  6s
Rendering [=======================================>------------------------------------] at 8.2 fps ~ eta:  6s
Rendering [=======================================>------------------------------------] at 8.1 fps ~ eta:  6s
Rendering [========================================>-----------------------------------] at 8.1 fps ~ eta:  6s
Rendering [=========================================>----------------------------------] at 8.2 fps ~ eta:  6s
Rendering [==========================================>---------------------------------] at 8.2 fps ~ eta:  5s
Rendering [==========================================>---------------------------------] at 8.1 fps ~ eta:  5s
Rendering [===========================================>--------------------------------] at 8.1 fps ~ eta:  5s
Rendering [============================================>-------------------------------] at 8.1 fps ~ eta:  5s
Rendering [=============================================>------------------------------] at 8.1 fps ~ eta:  5s
Rendering [==============================================>-----------------------------] at 8.1 fps ~ eta:  5s
Rendering [===============================================>----------------------------] at 8.1 fps ~ eta:  5s
Rendering [=================================================>----------------------------] at 8 fps ~ eta:  5s
Rendering [==================================================>---------------------------] at 8 fps ~ eta:  4s
Rendering [===================================================>--------------------------] at 8 fps ~ eta:  4s
Rendering [====================================================>-------------------------] at 8 fps ~ eta:  4s
Rendering [===================================================>------------------------] at 6.6 fps ~ eta:  5s
LS0tCnRpdGxlOiAiVHJhYmFqbyBQcmFjdGljbyAtIExhYm9yYXRvcmlvIGRlIERhdG9zIgphdXRob3I6ICJNb3JhbGVzIEpvYXF1aW4sIExhc29yc2EgTGF1dGFybyB5IFBhbGF6em8gVG9tYXMiCmRhdGU6ICIxNC8xMC8yMDIxIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRoZW1lOiBsdW1lbgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB5ZXMKc3VidGl0bGU6ICJMYWJvcmF0b3JpbyBkZSBEYXRvcyIKLS0tCiMgTGlicmVyaWFzClByaW1lcm8gaW5jb3Jwb3JhbW9zIGxhcyBsaWJyZXJpYXMgbmVjZXNhcmlhcyBwYXJhIHBvZGVyIGVqZWN1dGFyIGVsIHByZXNlbnRlIG5vdGVib29rLgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIExpYnJlcmlhcwojaW5zdGFsbC5wYWNrYWdlcygnZ2dhbmltYXRlJykKI2luc3RhbGwucGFja2FnZXMoImhlYXRtYXBseSIpCiNpbnN0YWxsLnBhY2thZ2VzKCdnaWZza2knKQojaW5zdGFsbC5wYWNrYWdlcygncG5nJykKI2luc3RhbGwucGFja2FnZXMoJ3VuaXRzJykKI2luc3RhbGwucGFja2FnZXMoJ3NmJykKI2luc3RhbGwucGFja2FnZXMoJ3RyYW5zZm9ybXInKQpyZXF1aXJlKHRpZHl2ZXJzZSkKcmVxdWlyZShnZW9zcGhlcmUpCiM8PDw8PDw8IFVwZGF0ZWQgdXBzdHJlYW0KcmVxdWlyZShydmVzdCkKcmVxdWlyZShnZ2FuaW1hdGUpCnJlcXVpcmUobHVicmlkYXRlKQpyZXF1aXJlKGhlYXRtYXBseSkKcmVxdWlyZSh0cmFuc2Zvcm1yKQpyZXF1aXJlKGdpZnNraSkKcmVxdWlyZShwbmcpCnJlcXVpcmUoZ2dwbG90MikKIz09PT09PT0KcmVxdWlyZShydmVzdCkgIyBDYXJnYW1vcyBlbCBwYXF1ZXRlCnJlcXVpcmUobHVicmlkYXRlKQojPj4+Pj4+PiBTdGFzaGVkIGNoYW5nZXMKYGBgCiMgSW1wb3J0YXIgZGF0b3MKClBvc3Rlcmlvcm1lbnRlIGltcG9ydGFtb3MgbG9zIGRhdG9zIHF1ZSBxdWVyZW1vcyBhbmFsaXphciB5IGxvcyBmb3JtYXRlYW1vcy4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgTGxhbWFtb3MgYSBsb3MgRGF0YVNldHMKCmRhdG9zXzIwMjEgPSByZWFkLmNzdignLi4vVFBfTGFib0RlRGF0b3MvRGF0YS9zbmFfYWJyaWxfMjAyMV9maXhlZF9lbmNvZGluZy5jc3YnLCBlbmNvZGluZyA9ICdVVEYtOCcsIHNlcCA9ICcsJykKCiMgQ29uY2F0ZW5vIHRvZG9zIGxvcyBkYXRvcwpkYXRvczI9IHJlYWRfY3N2MignLi4vVFBfTGFib0RlRGF0b3MvRGF0YS8yMDIxMDktaW5mb3JtZS1taW5pc3RlcmlvLmNzdicpCgpkYXRvczIkRmVjaGFfY29tcGxldGEgPSBzdHJwdGltZShwYXN0ZShkYXRvczIkRmVjaGEsIGRhdG9zMiRgSG9yYSBVVENgKSwgZm9ybWF0ID0gIiVkLyVtLyVZICVIOiVNOiVTIikKCmBgYAoKRW4gZXNhIGNlbGRhIGltcG9ydGFtb3MgbG9zIGRhdG9zIGRlIGxvcyBhZXJvcHVlcnRvcy4KYGBge3J9CiMgVXRpbGl6YW1vcyBsYSB0YWJsYSBxdWUgc2UgZW5jdWVudHJhIGVuICdodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MaXN0X29mX2FpcnBvcnRzX2luX0FyZ2VudGluYScKIyBwYXJhIGFjY2VkZXIgYSBsYXMgdmFyaWFibGVzIGRlIGNpdWRhZCwgcHJvdmluY2lhIHkgY29vcmRlbmFkYXMgZGUgY2FkYSBhZXJvcHVlcnRvIAoKYWVyb3B1ZXJ0b3Nfd2lraSA9IHJlYWRfaHRtbCgnaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGlzdF9vZl9haXJwb3J0c19pbl9BcmdlbnRpbmEnKQplbGVtZW50b190YWJsYSAgID0gaHRtbF9lbGVtZW50KGFlcm9wdWVydG9zX3dpa2ksJy53aWtpdGFibGUnKQphZXJvcHVlcnRvcyAgICAgID0gaHRtbF90YWJsZShlbGVtZW50b190YWJsYSkKCmBgYAoKUGFydGljdWxhcm1lbnRlLCBuZWNlc2l0YW1vcyB1biBwcm9jZXNhbWllbnRvIGVzcGVjaWFsIHBhcmEgb2J0ZW5lciBsYXMgY29vcmRlbmFkYXMgZGUgY2FkYSBhZXJvcHVlcnRvIGVuIHVuIGZvcm1hdG8gcXVlIG5vcyBzZWEgdXRpbC4KCmBgYHtyfQojIENvcnJpam8gbGEgY29sdW1uYSBkZSBjb29yZGVuYWRhcyAKCnNlcGFyYXIgICAgID0gc3Ryc3BsaXQoKGFlcm9wdWVydG9zJENvb3JkaW5hdGVzKSwgJy8nKSAjIERpdmlkZSBhIGxvcyBzdHJpbmdzIGVuIGxvcyBsdWdhcmVzIGRvbmRlIGhheWEgJy8nIApjb29yZGVuYWRhcyA9IHNhcHBseShzZXBhcmFyLCBmdW5jdGlvbih4KSB4WzNdKSAgICAgICAgIyBNZSBxdWVkbyBzb2xvIGNvbiBlbCAzZXIgdGlwbyBkZSBjb29yZGVuYWRhCmNvb3JkZW5hZGFzID0gZ3N1YignW14wLTksLiwtXScsJycsIGNvb3JkZW5hZGFzKSAgICAgICAgICAgIyBFbGltaW5vIGxvcyBjYXJhY3RlcmVzIHF1ZSBubyBxdWllcm8gdXRpbGl6YXIKCmFlcm9wdWVydG9zID0gYWVyb3B1ZXJ0b3MgJT4lIAogIG11dGF0ZShsYXQgPSBhcy5udW1lcmljKHN1YnN0cihjb29yZGVuYWRhcywgMSwgOSkpLCBsb25nID0gYXMubnVtZXJpYyhzdWJzdHIoY29vcmRlbmFkYXMsIDEwLCAxOCkpKSAjIFNlcGFybyBhIG1hbm8gbGF0aXR1ZCB5IGxvbmdpdHVkIChyZXZpemFyIHNpIGVzdGEgdG9kbyBlbiBvcmRlbikKCmFlcm9wdWVydG9zID0gZmlsdGVyKGFlcm9wdWVydG9zLCBuY2hhcihJQ0FPKT4xKQphZXJvcHVlcnRvcyA9IGFlcm9wdWVydG9zW29yZGVyKGFlcm9wdWVydG9zJElDQU8pLF0KYGBgCgojIFByb2Nlc2FyIGRhdG9zCgpFbiBsYSBzaWd1aWVudGUgY2VsZGEgbG8gcXVlIGhhY2Vtb3MgZXMgYSBjYWRhIHZ1ZWxvIGxlIGFncmVnYW1vcyBsb3MgZGF0b3MgcmVsYXRpdm9zIGEgc3VzIGFlcm9wdWVydG9zIGRlIG9yaWdlbiB5IGRlc3Rpbm8gKHNpIGxvcyB0ZW5lbW9zKS4gQWRlbcOhcywgY2FsY3VsYW1vcyBjb24gZWxsb3MgbGEgZGlzdGFuY2lhIHJlY29ycmlkYSBwb3IgZWwgdnVlbG8uCgpgYGB7cn0KCmZvcihpIGluIDE6bGVuZ3RoKGFlcm9wdWVydG9zJElDQU8pKXsKICBpbmRzID0gZGF0b3MyJEFlcm9wdWVydG89PWFlcm9wdWVydG9zJElBVEFbaV0KICBkYXRvczJbaW5kcyxjKCdjaXVkYWRfb3JpZ2VuJywncHJvdmluY2lhX29yaWdlbicsJ2xhdF9vcmlnZW4nLCdsb25nX29yaWdlbicpXSA9IGFlcm9wdWVydG9zW2ksYygiQ2l0ecKgc2VydmVkIiwiUHJvdmluY2UiLCJsYXQiLCJsb25nIildCiAgCiAgaW5kcyA9IGRhdG9zMiRgT3JpZ2VuIC8gRGVzdGlub2A9PWFlcm9wdWVydG9zJElBVEFbaV0KICBkYXRvczJbaW5kcyxjKCdjaXVkYWRfZGVzdGlubycsJ3Byb3ZpbmNpYV9kZXN0aW5vJywnbGF0X2Rlc3Rpbm8nLCdsb25nX2Rlc3Rpbm8nKV0gPSBhZXJvcHVlcnRvc1tpLGMoIkNpdHnCoHNlcnZlZCIsIlByb3ZpbmNlIiwibGF0IiwibG9uZyIpXQp9CiAgZm9yKGkgaW4gMTpsZW5ndGgoZGF0b3NfMjAyMSRhbmEpKXsKICBpbmRzID0gZGF0b3MyJEFlcm9wdWVydG89PWRhdG9zXzIwMjEkYW5hW2ldCiAgZGF0b3MyW2luZHMsYygnY2l1ZGFkX29yaWdlbicsJ3Byb3ZpbmNpYV9vcmlnZW4nLCdsYXRfb3JpZ2VuJywnbG9uZ19vcmlnZW4nKV0gPSBkYXRvc18yMDIxW2ksYygibmFtIiwiY3ByIiwieCIsInkiKV0KICAKICAKICBpbmRzID0gZGF0b3MyJGBPcmlnZW4gLyBEZXN0aW5vYD09ZGF0b3NfMjAyMSRhbmFbaV0KICBkYXRvczJbaW5kcyxjKCdjaXVkYWRfZGVzdGlubycsJ3Byb3ZpbmNpYV9kZXN0aW5vJywnbGF0X2Rlc3Rpbm8nLCdsb25nX2Rlc3Rpbm8nKV0gPSBkYXRvc18yMDIxW2ksYygibmFtIiwiY3ByIiwieCIsInkiKV0KICAKICBpbmRzID0gZGF0b3MyJEFlcm9wdWVydG89PWRhdG9zXzIwMjEkaWtvW2ldCiAgZGF0b3MyW2luZHMsYygnY2l1ZGFkX2Rlc3Rpbm8nLCdwcm92aW5jaWFfZGVzdGlubycsJ2xhdF9kZXN0aW5vJywnbG9uZ19kZXN0aW5vJyldID0gZGF0b3NfMjAyMVtpLGMoIm5hbSIsImNwciIsIngiLCJ5IildCiAgCiAgaW5kcyA9IGRhdG9zMiRgT3JpZ2VuIC8gRGVzdGlub2A9PWRhdG9zXzIwMjEkaWtvW2ldCiAgZGF0b3MyW2luZHMsYygnY2l1ZGFkX2Rlc3Rpbm8nLCdwcm92aW5jaWFfZGVzdGlubycsJ2xhdF9kZXN0aW5vJywnbG9uZ19kZXN0aW5vJyldID0gZGF0b3NfMjAyMVtpLGMoIm5hbSIsImNwciIsIngiLCJ5IildCn0KCmRhdG9zMiA9IGRyb3BfbmEoZGF0b3MyKQpkYXRvczIkZGlzdGFuY2lhID0gZGlzdEhhdmVyc2luZShkYXRvczJbLGMoImxvbmdfb3JpZ2VuIiwibGF0X29yaWdlbiIpXSxkYXRvczJbLGMoImxvbmdfZGVzdGlubyIsImxhdF9kZXN0aW5vIildKS8xMDAwCgpkYXRvczIgPSBkYXRvczIgJT4lIAogIGZpbHRlcihgQWVyb2xpbmVhIE5vbWJyZWAgIT0gMCkKYGBgCgpEaXZpZGltb3MgbG9zIGV2ZW50b3MgZGVsIGRhdGFzZXQgZGUgdnVlbG9zIGVuIGF0ZXJyaXphamVzIHkgZGVzcGVndWVzLgoKYGBge3J9CgpkYXRvczIkZmVjaGFfaG9yYSA9IHN0cnB0aW1lKHBhc3RlKGRhdG9zMiRGZWNoYSwgZGF0b3MyJGBIb3JhIFVUQ2ApLCBmb3JtYXQgPSAiJWQvJW0vJVkgJUg6JU06JVMiKQpkZXNwZWd1ZXMgPSBkYXRvczJbZGF0b3MyJGBUaXBvIGRlIE1vdmltaWVudG9gID09ICdEZXNwZWd1ZScsXQphdGVycml6YWplcyA9IGRhdG9zMltkYXRvczIkYFRpcG8gZGUgTW92aW1pZW50b2AgPT0gJ0F0ZXJyaXphamUnLF0KCmBgYAoKQWhvcmEgdHJhdGFtb3MgZGUgdW5pciBsb3MgYXRlcnJpemFqZXMgeSBkZXNwZWd1ZXMgY29uIGVsIG9iamV0aXZvIGRlIHBvZGVyIGlkZW50aWZpY2FyIHRvZG8gZWwgcmVjb3JyaWRvIGRlIHVuIHZ1ZWxvLiAocGFydGljdWxhcm1lbnRlIGxhIGhvcmEgZGUgc2FsaWRhIHkgZGUgbGxlZ2FkYSwgcXVlIHNvbiBsb3MgZGF0b3MgcXVlIG5vIG5vcyBkYW4gZGlyZWN0YW1lbnRlIGVuIGVsIGRhdGFzZXQpCgpgYGB7cn0KIyBkaXZpZGltb3MgZW4gZGVzcGVndWVzIHkgYXRlcnJpemFqZXMKCgptYXRjaGVkID0gbGVmdF9qb2luKGRlc3BlZ3VlcywgYXRlcnJpemFqZXMsIGJ5PSBjKCJBZXJvcHVlcnRvIiA9ICJPcmlnZW4gLyBEZXN0aW5vIiwgIk9yaWdlbiAvIERlc3Rpbm8iID0gIkFlcm9wdWVydG8iLCAiQWVyb2xpbmVhIE5vbWJyZSIgPSAiQWVyb2xpbmVhIE5vbWJyZSIsICJBZXJvbmF2ZSIgPSAiQWVyb25hdmUiKSkgJT4lIAogIG11dGF0ZSh0ZGlmID0gYXMubnVtZXJpYyhmZWNoYV9ob3JhLnkgLSBmZWNoYV9ob3JhLngsIHVuaXRzPSdob3VycycpKSAlPiUKICBncm91cF9ieShBZXJvcHVlcnRvLCBmZWNoYV9ob3JhLngsIEFlcm9uYXZlLCBgQWVyb2xpbmVhIE5vbWJyZWApICU+JQogIGZpbHRlcih0ZGlmID4gMCkgJT4lIAogIGZpbHRlcih0ZGlmIDwgNSkgJT4lIAogIGZpbHRlcih0ZGlmID09IG1pbih0ZGlmKSkKCm1hdGNoZWQgPSBtYXRjaGVkICU+JQogIG11dGF0ZSh2ZWxfbWVkaWEgPSBkaXN0YW5jaWEueS90ZGlmKQoKYGBgCiMgUHJlZ3VudGFzCiMjIyBDb25jZW50cmFjacOzbiBkZWwgbWVyY2FkbwpMYXMgcHJpbWVyYXMgcHJlZ3VudGFzIHF1ZSBub3MgaGVtb3MgcGxhbnRlYWRvIHNvbiByZWxhdGl2YXMgYWwgZ3JhZG8gZGUgY29uY2VudHJhY2nDs24gcXVlIHRpZW5lbiBsb3MgdnVlbG9zLgoKUG9yIGVzdG8sIGxhIHByaW1lcmEgcHJlZ3VudGEgcXVlIG5vcyBpbnRlcmVzYSByZXNwb25kZXIgZXMgdmVyIGN1YWxlcyBhZXJvcHVlcnRvcyBzZSBjb25lY3RhbiBtw6FzIHkgY3VhbGVzIG1lbm9zLiBBZGVtw6FzLCBub3RhbmRvIHF1ZSBsb3MgY2Fzb3MgZG9uZGUgZWwgdnVlbG8gZGVzcGVnYSB5IGF0ZXJyaXphIGVuIGVsIG1pc21vIGx1Z2FyIHNvbiBtdXkgcG9zaWJsZW1lbnRlIHZ1ZWxvcyBkZSBlbnRyZW5hbWllbnRvLgpgYGB7cn0KYWVycyA9IHVuaXF1ZShkYXRvczIkQWVyb3B1ZXJ0bykKbWF0ID0gbWF0cml4KG5yb3cgPSBsZW5ndGgoYWVycyksIG5jb2wgPSBsZW5ndGgoYWVycykpCmNvbG5hbWVzKG1hdCkgPSBhZXJzCnJvdy5uYW1lcyhtYXQpID0gYWVycwpmb3IoaSBpbiAxOmxlbmd0aChhZXJzKSl7CiAgZm9yKGogaW4gMTpsZW5ndGgoYWVycykpewogICAgbWF0W2ksal0gID0gIHN1bSgoZGF0b3MyJEFlcm9wdWVydG89PWFlcnNbaV0pICYgKGRhdG9zMiRgT3JpZ2VuIC8gRGVzdGlub2A9PWFlcnNbal0pKQogIH0KfQpoZWF0bWFwbHkobWF0LCBSb3d2ID0gTkEsIENvbHYgPSBOQSwgIGNvbF9kZW5kX3VwPUZBTFNFKSU+JSAKICBsYXlvdXQoeGF4aXMgPSBsaXN0KHNpZGUgPSAidG9wIikpCmBgYApBaG9yYSB2YW1vcyBhIHZlciBxdcOpIG9jdXJyZSBjb24gZWwgZ3JhZmljbyBhbnRlcmlvciBzaSBsbyBwYXNhbW9zIGEgZXNjYWxhIGxvZ2FyaXRtaWNhIChhZ3JlZ2FuZG8gMSBwYXJhIGV2aXRhciBsb3MgLWluZikKCmBgYHtyfQptYXQyID0gbG9nKG1hdCsxKQpoZWF0bWFwbHkobWF0MiwgUm93diA9IE5BLCBDb2x2ID0gTkEsICBjb2xfZGVuZF91cD1GQUxTRSklPiUgCiAgbGF5b3V0KHhheGlzID0gbGlzdChzaWRlID0gInRvcCIpKQpgYGAKQ29tbyBzZSBwdWVkZSB2ZXIsIGhheSBtYXlvciBjYW50aWRhZCBkZSByZWxhY2lvbmVzIHF1ZSBzZSB2dWVsdmVuIHZpc2libGVzIHkgZGlmZXJlbmNpYWJsZXMgZ3JhY2lhcyBhbCBjYW1iaW8gZGUgZXNjYWxhLgoKUmVzYWx0YSBlbCBhZXJvcHVlcnRvIEZETyBjb21vIHVuIGltcG9ydGFudGUgY2VudHJvIGRlIGVudHJlbmFtaWVudG8uIFBvciBvdHJvIGxhZG8sIHNlIG9ic2VydmEgY29tbyBsYSBjb25leGnDs24gbcOhcyBmdWVydGUgZXMgY2xhcmFtZW50ZSBlbnRyZSBBZXJvcGFycXVlIHkgQmFyaWxvY2hlLiAKCkxhIHNpZ3VpZW50ZSBwcmVndW50YSBzZXLDoSByZWxhdGl2YSBhIGxhIGNvbmNlbnRyYWNpw7NuIGVudHJlIGRpc3RpbnRhcyBhZXJvbGluZWFzLiAKCmBgYHtyfQphZXJvbGluZWFzID0gdW5pcXVlKG1hdGNoZWQkYEFlcm9saW5lYSBOb21icmVgKQphcHBzID0gbWF0cml4KG5yb3cgPSAxLCBuY29sID0gbGVuZ3RoKGFlcm9saW5lYXMpKQpjb2xuYW1lcyhhcHBzKSA9IGFlcm9saW5lYXMKZm9yKGFyIGluIGFlcm9saW5lYXMpewogIGFwcHNbMSxhcl0gPSBzdW0oKG1hdGNoZWQkYEFlcm9saW5lYSBOb21icmVgPT1hcikpCn0KYXBwcyA9IGFwcHNbMSxvcmRlcihhcHBzLCBkZWNyZWFzaW5nPVQpXQojYmFycGxvdChhcHBzLCBsYXMgPSAyLCBjZXgubmFtZXM9MC40KQplamV5IDwtIGxpc3QodGl0bGU9IkFlcm9saW5lYXMiLCBjYXRlZ29yeW9yZGVyID0gImFycmF5IiwKICAgICAgICAgICAgICBjYXRlZ29yeWFycmF5ID0gcmV2KG5hbWVzKGFwcHMpKSwgdGlja2ZvbnQgPSBsaXN0KHNpemU9NCkpCmRmYXBwcyA9IGRhdGEuZnJhbWUobmFtZXMoYXBwcyksYXBwcykKZGZhcHBzID0gcmVuYW1lKGRmYXBwcyxBZXJvbGluZWFzID0gbmFtZXMuYXBwcy4sIENhbnRpZGFkID0gYXBwcykKZmlnIDwtIHBsb3RfbHkoZGZhcHBzLCB4ID0gfkNhbnRpZGFkLCB5ID0gfkFlcm9saW5lYXMsIHR5cGUgPSAiYmFyIikgJT4lIAogbGF5b3V0KHlheGlzID0gZWpleSkKCmZpZwpgYGAKWSBhaG9yYSBwcm9iYW1vcyBkZSByZXBldGlyIGVsIGVqZXJjaWNpbyBkZSBleHByZXNhciBsYXMgZGlzdGludGFzIGNhbnRpZGFkZXMgZW4gZXNjYWxhIGxvZ2FyaXRtaWNhIHBhcmEgdmVyIGVsIGVmZWN0by4KYGBge3J9CmFwcHNsb2cgPSBsb2coYXBwcysxKQojYmFycGxvdChhcHBzLCBsYXMgPSAyLCBjZXgubmFtZXM9MC40KQplamV5IDwtIGxpc3QodGl0bGU9IkFlcm9saW5lYXMiLCBjYXRlZ29yeW9yZGVyID0gImFycmF5IiwKICAgICAgICAgICAgICBjYXRlZ29yeWFycmF5ID0gcmV2KG5hbWVzKGFwcHMpKSwgdGlja2ZvbnQgPSBsaXN0KHNpemU9NCkpCmRmYXBwcyA9IGRhdGEuZnJhbWUobmFtZXMoYXBwc2xvZyksYXBwc2xvZykKZGZhcHBzID0gcmVuYW1lKGRmYXBwcyxBZXJvbGluZWFzID0gbmFtZXMuYXBwc2xvZy4sIENhbnRpZGFkID0gYXBwc2xvZykKZmlnIDwtIHBsb3RfbHkoZGZhcHBzLCB4ID0gfkNhbnRpZGFkLCB5ID0gfkFlcm9saW5lYXMsIHR5cGUgPSAiYmFyIikgJT4lIAogbGF5b3V0KHlheGlzID0gZWpleSkKZmlnCmBgYApFbiBlc3RlIG51ZXZvIGdyYWZpY28gc2UgY29uZmlybWEgbGEgaWRlYSBkZSBxdWUgaGF5IHVuYSBlbm9ybWUgY29uY2VudHJhY2nDs24gZGUgbG9zIHZ1ZWxvcyBlbiBwb2NhcyBlbXByZXNhcy4KCkVuIGVzdGUgdGVyY2VyIGdyYWZpY28gc29sbyBtb3N0cmFtb3MgbGFzIG3DoXMgaW1wb3J0YW50ZXMgcGFyYSBxdWUgc2VhIGVudGVuZGlibGUgbGEgaW5mb3JtYWNpw7NuLCBhZGVtw6FzIGRlIHF1ZSB0aWVuZSB1bmEgZnVuY2nDs24gbcOhcyBzaW50ZXRpY2EgcXVlIGxvcyBhbnRlcmlvcmVzLiAKCmBgYHtyfQp4ID0gOAphcHBzMiA9IGFwcHNbMTp4XQphcHBzMlt4KzFdID0gc3VtKGFwcHNbeDpsZW5ndGgoYXBwcyldKQpuYW1lcyhhcHBzMilbeCsxXSA9ICJPVFJBUyIKbmFtZXMoYXBwczIpID0gcGFzdGUobmFtZXMoYXBwczIpLGFwcHMyKQoKZmlnIDwtIHBsb3RfbHkoZGF0YSA9IGFzLmRhdGEuZnJhbWUoYXBwczIpLCBsYWJlbHMgPSBuYW1lcyhhcHBzMiksIHZhbHVlcyA9IGFwcHMyLCB0eXBlID0gJ3BpZScsIHNvcnQ9RkFMU0UpCmZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZSA9ICdDYW50aWRhZCBkZSB2dWVsb3MgcG9yIGFlcm9saW5lYScsCiAgICAgICAgIHhheGlzID0gbGlzdChzaG93Z3JpZCA9IEZBTFNFLCB6ZXJvbGluZSA9IEZBTFNFLCBzaG93dGlja2xhYmVscyA9IEZBTFNFKSwKICAgICAgICAgeWF4aXMgPSBsaXN0KHNob3dncmlkID0gRkFMU0UsIHplcm9saW5lID0gRkFMU0UsIHNob3d0aWNrbGFiZWxzID0gRkFMU0UpKSAKZmlnCmBgYAoKIyMjIFRpZW1wbyBlbiBlbCBhaXJlCkxhcyBzaWd1aWVudGVzIHByZWd1bnRhcyBxdWUgbm9zIGhlbW9zIHBsYW50ZWFkbyBmZXJvbiBsYXMgcmVsYXRpdmFzIGEgY3VhbmRvIGxvcyBhdmlvbmVzIGVzdGFiYW4gdm9sYW5kbyB5IGN1YW5kbyBuby4KCkxhIHByaW1lcmEgZnVlIGRlY2lyIGV4YWN0YW1lbnRlIGVuIHF1w6kgaG9yYXMgZGVsIGHDsW8gZXN0dXZpZXJvbiBtw6FzIHkgbWVub3MgYXZpb25lcyBlbiBlbCBhaXJlLiBQYXJhIGVzbyBwcmltZXJvIGhheSBxdWUgYWdyZWdhciB1bmEgaG9yYSB1bml2ZXJzYWwgYSBsb3MgdnVlbG9zCgpgYGB7cn0KaW5pY2lvID0gYXMuRGF0ZSgiMjAyMS0wMS0wMSIpCm1hdGNoZWQkZGVzcGVndWVVbml2ZXJzYWwgPSBhcy5pbnRlZ2VyKC1kaWZmdGltZShpbmljaW8sbWF0Y2hlZCRGZWNoYV9jb21wbGV0YS54LCB1bml0cz0iaG91cnMiKSkKbWF0Y2hlZCRsbGVnYWRhVW5pdmVyc2FsID0gYXMuaW50ZWdlcigtZGlmZnRpbWUoaW5pY2lvLG1hdGNoZWQkRmVjaGFfY29tcGxldGEueSwgdW5pdHM9ImhvdXJzIikrMC45OTk5KQpmaW4gPSBtYXgobWF0Y2hlZCRsbGVnYWRhVW5pdmVyc2FsKSsxCmhpc3RvID0gbWF0cml4KG5yb3c9MSxuY29sPWZpbikKaGlzdG9bLGMoMTpmaW4pXSA9IDAKZm9yKGkgaW4gMTpsZW5ndGgobWF0Y2hlZCRkZXNwZWd1ZVVuaXZlcnNhbCkpewogIGluZHMgPSBtYXRjaGVkJGRlc3BlZ3VlVW5pdmVyc2FsW2ldOm1hdGNoZWQkbGxlZ2FkYVVuaXZlcnNhbFtpXQogIGhpc3RvWzEsaW5kc10gPSBoaXN0b1sxLGluZHNdKzEKICAjcHJpbnQoaGlzdG9bMSxpbmRzXSkKICAjZm9yKGogaW4gbWF0Y2hlZCRkZXNwZWd1ZVVuaXZlcnNhbFtpXTptYXRjaGVkJGxsZWdhZGFVbml2ZXJzYWxbaV0pewogICMgIGhpc3RvWzEsal0gPSBoaXN0b1sxLGpdKzEKICAjfQp9CjUlJTIKI3Bsb3QoMTpsZW5ndGgoaGlzdG9bMSxdKSxoaXN0b1sxLF0sIHR5cGU9InMiKQp0cz0gcGFzdGUoKGluaWNpbyArIDE6ZmluLzI0KSwgYXMuY2hhcmFjdGVyKDE6ZmluJSUyNCkpCmVqZSA8LSBsaXN0KHRpdGxlPSJIb3JhIiwgY2F0ZWdvcnlvcmRlciA9ICJhcnJheSIsCiAgICAgICAgICAgICAgY2F0ZWdvcnlhcnJheSA9IGFzLmNoYXJhY3Rlcih0cyksIHRpY2tmb250ID0gbGlzdChzaXplPTUpKQpwcmludCh0eXBlb2YoZWplJGNhdGVnb3J5YXJyYXkpKQp0ZW1wID0gZGF0YS5mcmFtZShoaXN0b1sxLF0sRmVjaGEgPWVqZSRjYXRlZ29yeWFycmF5KQp0ZW1wID0gcmVuYW1lKHRlbXAsQ2FudGlkYWQgPSBoaXN0by4xLi4uKQpmaWcgPC0gcGxvdF9seSh0ZW1wLCB5ID0gfkNhbnRpZGFkLCB4ID0gfkZlY2hhLCB0eXBlID0gImJhciIpICU+JSAKIGxheW91dCh4YXhpcyA9IGVqZSkKCmZpZwoKYGBgCgpQYXJhIGV2aXRhciBlbCBydWlkbyB5IGxhcyBwZXF1ZcOxYXMgdmFyaWFjaW9uZXMgZGlhcmlhcyB5IHNlbWFuYWxlcywgdmFtb3MgYSByZXBldGlyIGVsIGdyYWZpY28gc3Vhdml6YWRvIHBvciBkw61hIHkgcG9yIHNlbWFuYS4KCmBgYHtyfQoKI3Bsb3QoMTpsZW5ndGgoaGlzdG9bMSxdKSxoaXN0b1sxLF0sIHR5cGU9InMiKQoKaGlzdG8yID0gbWF0cml4KG5yb3cgPSAxLCBuY29sID0gbGVuZ3RoKGhpc3RvWzEsXSktMjMpCmxlbmd0aChoaXN0bzIpCmZvcihpIGluIDI0OihmaW4tMjMpKXsKICAjcHJpbnQoKGktMjMpOmkpCiAgaGlzdG8yWzEsaS0yM10gPSBzdW0oaGlzdG9bMSwoaS0yMyk6aV0pLzI0Cn0KdHM9IHBhc3RlKChpbmljaW8gKyAyNDpmaW4vMjQpLCBhcy5jaGFyYWN0ZXIoMjQ6ZmluJSUyNCkpCmVqZSA8LSBsaXN0KHRpdGxlPSJIb3JhIiwgY2F0ZWdvcnlvcmRlciA9ICJhcnJheSIsCiAgICAgICAgICAgICAgY2F0ZWdvcnlhcnJheSA9IGFzLmNoYXJhY3Rlcih0cyksIHRpY2tmb250ID0gbGlzdChzaXplPTUpKQpwcmludCh0eXBlb2YoZWplJGNhdGVnb3J5YXJyYXkpKQp0ZW1wID0gZGF0YS5mcmFtZShoaXN0bzJbMSxdLEZlY2hhID1lamUkY2F0ZWdvcnlhcnJheSkKdGVtcCA9IHJlbmFtZSh0ZW1wLENhbnRpZGFkID0gaGlzdG8yLjEuLi4pCmZpZyA8LSBwbG90X2x5KHRlbXAsIHkgPSB+Q2FudGlkYWQsIHggPSB+RmVjaGEsICB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJykgJT4lIAogbGF5b3V0KHhheGlzID0gZWplKQoKZmlnCgpgYGAKYGBge3J9CgojcGxvdCgxOmxlbmd0aChoaXN0b1sxLF0pLGhpc3RvWzEsXSwgdHlwZT0icyIpCmxpbSA9IDI0KjcKaGlzdG8zID0gbWF0cml4KG5yb3cgPSAxLCBuY29sID0gbGVuZ3RoKGhpc3RvWzEsXSktbGltKzEpCmZvcihpIGluIGxpbTooZmluLWxpbSsxKSl7CiAgI3ByaW50KChpLTIzKTppKQogIGhpc3RvM1sxLGktbGltKzFdID0gc3VtKGhpc3RvWzEsKGktbGltKzEpOmldKS9saW0KfQp0cz0gcGFzdGUoKGluaWNpbyArIGxpbTpmaW4vMjQpLCBhcy5jaGFyYWN0ZXIobGltOmZpbiUlMjQpKQplamUgPC0gbGlzdCh0aXRsZT0iSG9yYSIsIGNhdGVnb3J5b3JkZXIgPSAiYXJyYXkiLAogICAgICAgICAgICAgIGNhdGVnb3J5YXJyYXkgPSBhcy5jaGFyYWN0ZXIodHMpLCB0aWNrZm9udCA9IGxpc3Qoc2l6ZT01KSkKcHJpbnQodHlwZW9mKGVqZSRjYXRlZ29yeWFycmF5KSkKdGVtcCA9IGRhdGEuZnJhbWUoaGlzdG8zWzEsXSxGZWNoYSA9ZWplJGNhdGVnb3J5YXJyYXkpCnRlbXAgPSByZW5hbWUodGVtcCxDYW50aWRhZCA9IGhpc3RvMy4xLi4uKQpmaWcgPC0gcGxvdF9seSh0ZW1wLCB5ID0gfkNhbnRpZGFkLCB4ID0gfkZlY2hhLCAgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycpICU+JSAKIGxheW91dCh4YXhpcyA9IGVqZSkKCmZpZwoKYGBgCkVuIGVsIGdyYWZpY28gY29uIGVsIHByb21lZGlvIHNlbWFuYWwgbW92aWwgc2UgcHVlZGUgYXByZWNpYXIgZXNwZWNpYWxtZW50ZSBsYSBjYWlkYSBlbiBsYSBjYW50aWRhZCBkZSBhdmlvbmVzIGVuIGVsIGFpcmUgZHVyYW50ZSBsYSBtaXRhZCBkZWwgYcOxbwoKQWhvcmEgY3VhbCBlcyBlbCBob3JhcmlvIGVuIGVsIHF1ZSBsb3MgcGFzYWplcm9zIHZpYWphbiBtYXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnJlcXVpcmUoZ2dhbmltYXRlKQpob3JhcmlvX3Z1ZWxvID0gZGF0b3MyICU+JQogIGZpbHRlcihkYXRvczIkUGFzYWplcm9zID4gMCkgJT4lIAogIGdyb3VwX2J5KGhvcmFyaW8gPSBob3VyKGBIb3JhIFVUQ2ApKSAlPiUgCiAgc3VtbWFyaXNlKHBhc2FqZXJvcyA9IG1lYW4oUGFzYWplcm9zKSkKCnRhYmxlKGhvcmFyaW9fdnVlbG8kaG9yYXJpbykKCnBsb3QgPSBob3JhcmlvX3Z1ZWxvICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBob3JhcmlvLCB5ID0gcGFzYWplcm9zLCBjb2xvciA9IHBhc2FqZXJvcykpICsgZ2VvbV9saW5lKCkgKwogICAgZ2VvbV9wb2ludCgpICsKICB0cmFuc2l0aW9uX3JldmVhbChob3JhcmlvKStsYWJzKHRpdGxlID0gJ0NhbnRpZGFkIHByb21lZGlvIGRlIHBhc2FqZXJvcyBhIGxvIGxhcmdvIGRlbCBkaWEnLCB4ID0gJ0hvcmEnLCB5ID0gJ0NhbnRpZGFkIHByb21lZGlvIGRlIHBhc2FqZXJvcycpKyBzaGFkb3dfbWFyaygpKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gcm91bmQoc2VxKG1pbihob3JhcmlvX3Z1ZWxvJGhvcmFyaW8pLCBtYXgoaG9yYXJpb192dWVsbyRob3JhcmlvKSwgYnkgPSAxKSwxKSkKYW5pbWF0ZShwbG90LCByZW5kZXJlciA9IGdpZnNraV9yZW5kZXJlcihsb29wID0gVCkpCgpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnBsb3QgPC0gaG9yYXJpb192dWVsbyAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gaG9yYXJpbywgeSA9IHBhc2FqZXJvcywgZmlsbCA9IHBhc2FqZXJvcykpICsgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSt0cmFuc2l0aW9uX3N0YXRlcyhob3JhcmlvLDEwMCw1KStzaGFkb3dfbWFyaygpK2xhYnModGl0bGUgPSAnQ2FudGlkYWQgcHJvbWVkaW8gZGUgcGFzYWplcm9zIGEgbG8gbGFyZ28gZGVsIGRpYScsIHggPSAnSG9yYScsIHkgPSAnQ2FudGlkYWQgcHJvbWVkaW8gZGUgcGFzYWplcm9zJykrIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSByb3VuZChzZXEobWluKGhvcmFyaW9fdnVlbG8kaG9yYXJpbyksIG1heChob3JhcmlvX3Z1ZWxvJGhvcmFyaW8pLCBieSA9IDEpLDEpKQphbmltYXRlKHBsb3QsIHJlbmRlcmVyID0gZ2lmc2tpX3JlbmRlcmVyKGxvb3AgPSBUKSkKCgpgYGA=